/** \file matriz.cpp
 *  \brief Implementação dos métodos, operadores e funções definidos na classe matriz, no arquivo matriz.hpp
 *  \author Geraldo Francisco de S. Rebouças | gfs.reboucas@gmail.com
 *  \since 28/11/2012
 *  \version 1.0
 *  \date 02/12/2012 */

#include "matriz.hpp"

matriz::matriz()        {}

matriz::~matriz()       {}

matriz::matriz(int ll, int cc)
{/** \fn matriz
  *  \param ll Quantidade de linhas da matriz a ser criada
  *  \param cc Quantidade de colunas da matriz a ser criada */

    l = ll;    c = cc;

    A.resize(l);

    for(signed int i = 0; i < l; i++)
        A[i].resize(c);
//        A[i].set(l, 'c');

    setNull(l, c);
}

matriz::matriz(valarray <vetor> M, int ll, int cc)
{/** \fn matriz
  *  \param M valarray de vetor contendo os elementos da matriz a ser criada
  *  \param ll Quantidade de linhas da matriz a ser criada
  *  \param cc Quantidade de colunas da matriz a ser criada */

    l = ll;     c = cc;

    A.resize(l);

    for(signed int i = 0; i < l; i++)
    {
        A[i].resize(c);
        for(signed int j = 0; j < c; j++)
            A[i][j] = M[i](j);
    }
}

matriz::matriz(valarray < valarray <double> > M, int ll, int cc)
{/** \fn matriz
  *  \param M valarray de double contendo os elementos da matriz a ser criada
  *  \param ll Quantidade de linhas da matriz a ser criada
  *  \param cc Quantidade de colunas da matriz a ser criada */

    l = ll;     c = cc;

    A.resize(l);

    for(signed int i = 0; i < l; i++)
        A[i].resize(c);

    A = M;
}

matriz::matriz(const matriz &M)     {    l = M.l;    c = M.c;    A = M.A;   }
/** \fn matriz
 *  \param M Matriz a ser copiada */

void matriz::setNull(int a, int b)
{
    l = a;      c = b;

    A.resize(l);

    for(signed int i = 0; i < l; i++)
    {
        A[i].resize(c);
        for(signed int j = 0; j < c; j++)
            A[i][j] = 0.0;
    }
}

void matriz::setIdent(int q)
{
    setNull(q, q);

    for(signed int i = 0; i < q; i++)
        A[i][i] = 1.0;
}

void matriz::setLine(vetor q, int w)
{
    if((q.retDim() != c) or (w > l - 1))
    {
        cerr << "setLine \nErro! Dimensoes incompativeis \n";
        abort();
    }

    for(signed int i = 0; i < c; i++)
        A[w][i] = q(i);
}

void matriz::setCol(vetor q, int w)
{
    if((q.retDim() != l) or (w > c - 1))
    {
        cerr << "setCol \nErro! Dimensoes incompativeis \n";
        abort();
    }

    for(signed int i = 0; i < l; i++)
        A[i][w] = q(i);
}

int matriz::retl()      { return l; }

int matriz::retc()      { return c; }

void matriz::show()
{
    for(int i = 0; i < l; i++)
    {
        for(int j = 0; j < c; j++)
            scout << A[i][j] << "\t";

        cout << endl;
    }
}

vetor matriz::lin(int ll)
{
    int cc = this->retc();
    vetor ret(cc,'l');

    if((ll < 0) || (ll > l - 1))
    {
        cerr << "Erro!  Indice da linha incompativel! \n";
        abort();
    }
    else
    {
        for(signed int i = 0; i < c; i++)
            ret(i) = A[ll][i];
    }
    return ret;
}

/** \fn lin
 *  \param ll Linha da matriz que será retornada */

vetor matriz::col(int cc)
{/** \fn col
  *  \param cc Coluna da matriz que será retornada */

    int ll = this->retl();
    vetor ret(ll,'c');

    if((cc < 0) || (cc > c - 1))
    {
        cerr << "Erro!  Indice da coluna incompativel! \n";
        abort();
    }
    else
    {
        for(signed int i = 0; i < l; i++)
            ret(i) = A[i][cc];
    }
    return ret;
}


matriz& matriz::operator+=(const matriz &M)
{
    *this = *this + M;
    return *this;
}

matriz& matriz::operator=(const matriz &M)
{
    if(this == &M)
    {
        ///
    }
    else
    {
        l = M.l;    c = M.c;    A = M.A;
    }

    return *this;
}

matriz matriz::operator-()  {   return -1.0*(*this);    }

double& matriz::operator()(int ll, int cc)      {   return A[ll][cc];    }

matriz operator*(double k, matriz M)
{
    int ll = M.retl();
    int cc = M.retc();
    matriz ret(ll, cc);

    for(signed int i = 0; i < ll; i++)
        for(signed int j = 0; j < cc; j++)
        ret(i,j) = k*M(i,j);

    return ret;
}

matriz operator*(matriz M, double k)    {   return k*M;     }

/** \fn *
 *  \brief Perceba que utilizamos a definição da sobrecarga anterior p/ tornarmos o código menor */

matriz operator*(matriz B, matriz C)
{
    int lb, cb, lc, cc;

    lb = B.retl();      cb = B.retc();
    lc = C.retl();      cc = C.retc();

    matriz ret(lb, cc);

    if(cb != lc)
    {
        cerr << "Erro! Dimensoes invalidas! \n";
        abort();
    }
    else
    {
        for(signed int i = 0; i < lb; i++)
            for(signed int j = 0; j < cc; j++)
                for(signed int k = 0; k < lb; k++)
                    ret(i,j) += B(i,k)*C(k,j);
    }

    return ret;
}

matriz operator+(matriz A, matriz B)
{
    int ll, cc;
    ll = A.retl();
    cc = B.retc();
    matriz ret(ll, cc);

    if((A.retl() != B.retl()) || (A.retc() != B.retc()))
    {
        cerr << "Erro! Dimensões incompatíveis! \n";
        abort();
    }
    else
    {
        for(signed int i = 0; i < ll; i++)
            for(signed int j = 0; j < cc; j++)
                ret(i,j) = A(i,j) + B(i,j);
    }

    return ret;
}

matriz operator-(matriz A, matriz B)    {   return A + (-B);    }
/** \fn -
 *  \brief Perceba que utilizamos a sobrecarga anterior, novamente para diminuir o número de linhas do código */

ostream& operator<<(ostream &os, matriz M)
{
    for(int i = 0; i < M.retl(); i++)
        for(int j = 0; j < M.retc(); j++)
            os << M(i, j) << "\t";

    return os;
}
